#ifndef _Ring_QUEUE_HPP_
#define _Ring_QUEUE_HPP_

#include <iostream>
#include <vector>
#include <pthread.h>
#include <semaphore.h> //信号量头文件

const int g_default_num = 5; // 设置环形队列大小

template <class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem); //--，如果申请不成功，就挂起
    }
    void V(sem_t &sem)
    {
        sem_post(&sem); //++
    }

public:
    RingQueue(int default_num = g_default_num)
        : ring_queue_(default_num), _num(default_num), c_step(0), p_step(0)
    {
        // 初始化锁
        pthread_mutex_init(&clock, nullptr);
        pthread_mutex_init(&plock, nullptr);

        // 初始化信号量
        sem_init(&space_sem_, 0, default_num);
        sem_init(&data_sem_, 0, 0);
    }
    ~RingQueue()
    {
        pthread_mutex_destroy(&clock);
        pthread_mutex_destroy(&plock);

        sem_destroy(&space_sem_);
        sem_destroy(&data_sem_);
    }
    // 为空或为满时，都只允许一个生产者或消费者来访问队列
    void push(const T &in) // 生产者：空间资源    问题：生产者们的临界资源是什么？ c_step下标，所以我们要加锁保护这个下标，消费者同理
    {
        // 问题：信号量是一种资源的预定机制，所以是先加锁还是先申请信号量？还是先申请信号量再加锁？
        // 1，技术角度：信号量资源不需要被保护，因为它本身就是原子的，不需要加锁；要尽量保证加锁解锁区域的代码小一些，能提高整体效率
        // 2，逻辑角度：先申请信号量再申请锁，可以在多线程申请时，使申请信号量和申请锁的时间变成并行的，能提高效率
        P(space_sem_); // 先申请空间资源，未申请成功就从这里挂起
        pthread_mutex_lock(&plock);
        // 走到这里一定是竞争成功的生产者 --> 就一个
        ring_queue_[p_step] = in;
        p_step++;
        p_step %= _num;
        pthread_mutex_unlock(&plock);
        V(data_sem_); // 生产了一个资源，就把资源信号量做V操作
    }
    void pop(T *out) // 消费者：数据资源
    {
        P(data_sem_); // 先申请数据资源
        pthread_mutex_lock(&clock);
        // 走到这里一定是竞争成功的消费者 --> 就一个
        *out = ring_queue_[c_step];
        c_step++;
        c_step %= _num;
        pthread_mutex_unlock(&clock);
        V(space_sem_); // 消费了一个数据，使空间++
    }

private:
    std::vector<T> ring_queue_;
    int _num;

    int c_step; // 消费下标
    int p_step; // 生产下标

    sem_t space_sem_; // 空间信号量
    sem_t data_sem_;  // 数据信号量

    pthread_mutex_t clock; // 消费者的锁
    pthread_mutex_t plock; // 生产者的锁
};

#endif